package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annocation.Autowired;
import com.lagou.edu.annocation.Service;
import com.lagou.edu.annocation.Transactional;
import net.sf.cglib.core.CollectionUtils;
import sun.jvm.hotspot.memory.Dictionary;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;

import static java.lang.Class.forName;

/**
 * @author: jie.zhao
 * @create: 2021/02/14 21:59
 */
@Service
public class AnnocationBeanFactory implements BeanFactory{

    private static HashMap<String,Object> objectHashMap = new HashMap<>();


    public AnnocationBeanFactory() {
        init();
    }

    public void init(){
        // 获取所有的class类
        List<String> classList = getPathClassList();
        // 注入到objectHashMap
        insertIntoMap(classList);
    }

    private void insertIntoMap(List<String> classList) {
        for (int i = 0; i < classList.size(); i++) {
            insertOneIntoMap(classList.get(i));
        }
    }

    private void insertOneIntoMap(String className){
        if(this.getClass().getName().equals(className)){
            return;
        }
        try {
            Class clazz = forName(className);
            String id = getId(clazz);
            if(id==null){
                return;
            }
            Object o = instanceObject(clazz);

            Field[] fields = clazz.getDeclaredFields();
            Boolean jump = true;
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                if(field.getAnnotation(Autowired.class)!=null){
                    jump = false;
                }
            }
            if(jump){
                if(objectHashMap.get(id)==null) {
                    objectHashMap.put(id, o);
                }
                return;
            }

            // 初始化属性值
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                if(field.getAnnotation(Autowired.class)!=null) {
                    String fieldId = "";
                    field.setAccessible(true);
                    Class<?> fieldClass = field.getType();
                    List<Class> classList = new ArrayList<>();
                    if (fieldClass.isInterface()) {
                        classList = getAllInterfaceAchieveClass(fieldClass);
                        fieldId = getId(classList.get(0));
                    } else {
                        fieldId = getId(fieldClass);
                    }
                    if (fieldId == null) {
                        continue;
                    }
                    Object obj = objectHashMap.get(fieldId);
                    if (obj == null) {
                        if (fieldClass.isInterface()) {
                            insertOneIntoMap(classList.get(0).getName());
                        }else {
                            insertOneIntoMap(fieldClass.getName());
                        }
//                        insertOneIntoMap(fieldClass.getName());
                        obj = objectHashMap.get(fieldId);
                    }
                    field.set(o, obj);
                }
            }
            if(objectHashMap.get(id)==null) {
                objectHashMap.put(id, o);
            }
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private Object instanceObject(Class clazz) {
        Object oldObject = null;
        try {
            oldObject = clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        Annotation annotation = clazz.getAnnotation(Transactional.class);
        if(annotation!=null){
            Object o = objectHashMap.get("proxyFactory");
            if(o==null){
                insertOneIntoMap("com.lagou.edu.factory.ProxyFactory");
                o = objectHashMap.get("proxyFactory");
            }
            ProxyFactory proxyFactory = (ProxyFactory) o;
            Object objProxy = proxyFactory.getJdkProxy(oldObject);
            objectHashMap.put(getId(clazz)+"P",objProxy);
        }
        return oldObject;
    }

    /**
     * 获取所有接口的实现类
     * @return
     */
    public static List<Class> getAllInterfaceAchieveClass(Class clazz){
        ArrayList<Class> list = new ArrayList<>();
        //判断是否是接口
        if (clazz.isInterface()) {
            try {
                ArrayList<Class> allClass = getAllClassByPath(clazz.getPackage().getName());
                /**
                 * 循环判断路径下的所有类是否实现了指定的接口
                 * 并且排除接口类自己
                 */
                for (int i = 0; i < allClass.size(); i++) {

                    //排除抽象类
                    if(Modifier.isAbstract(allClass.get(i).getModifiers())){
                        continue;
                    }
                    //判断是不是同一个接口
                    if (clazz.isAssignableFrom(allClass.get(i))) {
                        if (!clazz.equals(allClass.get(i))) {
                            list.add(allClass.get(i));
                        }
                    }
                }
            } catch (Exception e) {
                System.out.println("出现异常");
            }
        }
        return list;
    }

    /**
     * 从指定路径下获取所有类
     * @return
     */
    public static ArrayList<Class> getAllClassByPath(String packagename){
        ArrayList<Class> list = new ArrayList<>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packagename.replace('.', '/');
        try {
            ArrayList<File> fileList = new ArrayList<>();
            Enumeration<URL> enumeration = classLoader.getResources(path);
            while (enumeration.hasMoreElements()) {
                URL url = enumeration.nextElement();
                fileList.add(new File(url.getFile()));
            }
            for (int i = 0; i < fileList.size(); i++) {
                list.addAll(findClass(fileList.get(i),packagename));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return list;
    }


    /**
     * 如果file是文件夹，则递归调用findClass方法，或者文件夹下的类
     * 如果file本身是类文件，则加入list中进行保存，并返回
     * @param file
     * @param packagename
     * @return
     */
    private static ArrayList<Class> findClass(File file,String packagename) {
        ArrayList<Class> list = new ArrayList<>();
        if (!file.exists()) {
            return list;
        }
        File[] files = file.listFiles();
        for (File file2 : files) {
            if (file2.isDirectory()) {
                assert !file2.getName().contains(".");//添加断言用于判断
                ArrayList<Class> arrayList = findClass(file2, packagename+"."+file2.getName());
                list.addAll(arrayList);
            }else if(file2.getName().endsWith(".class")){
                try {
                    //保存的类文件不需要后缀.class
                    list.add(forName(packagename + '.' + file2.getName().substring(0,
                            file2.getName().length()-6)));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }



    private String getId(Class clazz) {
//        if (clazz.isInterface()) {
//            List<Class> classList = getAllInterfaceAchieveClass(clazz);
//            if(classList==null || classList.size()==0){
//                return null;
//            }
//            clazz = classList.get(0);
//        }

        Annotation annotation = clazz.getAnnotation(Service.class);
        // 无需注入
        if(annotation==null){
            return null;
        }
        Service service = (Service) annotation;
        String value = service.value();
        if(value==null || value.equals("")){
            return clazz.getSimpleName().substring(0,1).toLowerCase()+clazz.getSimpleName().substring(1);
        }
        return value;
    }


    private List<String> getPathClassList() {
        List<String> fileNameList = new ArrayList<>();

        File file3 = new File(AnnocationBeanFactory.class.getResource("/").getPath());
        setValue(file3,fileNameList);
        return fileNameList;
    }

    private void setValue(File file,List<String> filrNameList){
        if(!file.isDirectory()){
            String fileName = file.getAbsolutePath();
            if(fileName.endsWith(".class")) {
                String result = dealName(fileName);
                if(!StringUtils.isEmpty(result)) {
                    filrNameList.add(result);
                }
            }
            return;
        }
        File[] value = file.listFiles();
        for (File file1 : value) {
            if(file1.isDirectory()){
                setValue(file1,filrNameList);
            }
            String name = null;
            try {
                name = file1.getCanonicalPath();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(name.endsWith(".class")) {
                String result = dealName(name);
                if(!StringUtils.isEmpty(result)) {
                    filrNameList.add(result);
                }
            }
            continue;
        }

    }

    private String dealName(String fileName) {
        if(!fileName.contains(".class")){
            return null;
        }
        if(!fileName.contains("/classes/")){
            return null;
        }
        String name = fileName.substring(fileName.indexOf("/classes/")+9,fileName.indexOf(".class")).replaceAll("/",".");
        return name;
    }

    public static void main(String[] args) {
        AnnocationBeanFactory annocationBeanFactory = new AnnocationBeanFactory();
        annocationBeanFactory.getPathClassList();
    }



    public static Object getBean(String id) {
        String newId = id+"P";
        Object obj = objectHashMap.get(newId);
        if(obj!=null){
            return obj;
        }
        return objectHashMap.get(id);
    }

    @Override
    public void clear() {
        objectHashMap.clear();
    }


}
